package cn.ad.server.web.spring.service.actions.context;

import cn.ad.server.web.exception.AdServerMessageException;
import cn.ad.server.web.filter.MatcherChain;
import cn.ad.server.web.log.IAdServerLogger;
import cn.ad.server.web.spring.model.sts.STSAdRequest;
import cn.ad.server.web.spring.model.sts.STSAdResponse;
import cn.ad.server.web.spring.service.actions.command.AdRequestCommand;
import cn.ad.server.web.spring.service.actions.command.TrackCommand;
import cn.ad.server.web.spring.service.actions.handler.AdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.DefaultAdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.OriginAdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.VideoAdFormatHandler;
import cn.ad.server.web.spring.service.actions.handler.gdt.server.GDTMobileServerHandler;
import cn.ad.server.web.spring.service.actions.targeturl.ConvertTargetUrlHandler;
import cn.ad.server.web.spring.service.actions.targeturl.IConvertTargetUrl;
import cn.ad.server.biz.internal.AdService;
import cn.ad.server.biz.internal.IAdspaceService;
import cn.ad.server.domain.ad.AdMaterial;
import cn.ad.server.domain.ad.AdSpace;
import cn.ad.server.domain.ad.Ratio;
import cn.ad.server.domain.ad.Strategy;
import cn.ad.server.domain.enumration.AdFormat;
import cn.ad.server.domain.enumration.ReturnCode;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;

/**
 * 自有Server2Server广告请求处理类，提供处理AdRequest的相关方方法
 * 
 * @author dongbin.yu
 * @from 2016-05-24
 * @since V1.0 此类在v2.0版本后不在维护
 */
@Component
@Deprecated
public class STSAdProcessor {

	private static Logger logger = LoggerFactory.getLogger(STSAdProcessor.class);

	@Autowired
	private AdService adService;

	@Autowired
	private IAdspaceService adspaceService;

	@Autowired
	private IAdServerLogger loggerService;

	@Resource(name = "targetMatcherChain")
	private MatcherChain matcherChain;

	private boolean debugMode;

	@Resource
	private String serverAddress;

	@Resource(name = "targetUrlConvert")
	public Map<String, IConvertTargetUrl> targetUrlConvert;

	@Autowired
	private ConvertTargetUrlHandler convertTargetUrlHandler;

	@Autowired
	private GDTMobileServerHandler gdtMobileServerHandler;

	private static final DecimalFormat df = new DecimalFormat("###0.00");

	private static HashMap<AdFormat, AdFormatHandler> adFormatHandlers = new HashMap<>();

	static {

		adFormatHandlers.put(AdFormat.BANNER, new DefaultAdFormatHandler());
		adFormatHandlers.put(AdFormat.TABLE_SCREEN, new DefaultAdFormatHandler());
		adFormatHandlers.put(AdFormat.OPEN_SCREEN, new DefaultAdFormatHandler());
		adFormatHandlers.put(AdFormat.VIDEO, new VideoAdFormatHandler());
		adFormatHandlers.put(AdFormat.AD_NATIVE, new OriginAdFormatHandler());
		df.setRoundingMode(RoundingMode.HALF_UP);

	}

	public void process(STSAdRequest adRequest, STSAdResponse adResponse) {
//		try {
//			// 从redis取adSpace数据 key从redis里面获取
//			STSAdRequest.Impression[] impressions = adRequest.getImpression();
//
//			debugMode = adRequest.isDebugMode();
//
//			// set AdResponseData
//			STSAdResponse.AdResponseData data = new STSAdResponse.AdResponseData();
//			adResponse.setData(data);
//
//			STSAdResponse.AdSpace[] adSpaces = new STSAdResponse.AdSpace[impressions.length];
//
//			for (int i = 0; i < impressions.length; i++) {
//
//				STSAdRequest.Impression impression = impressions[i];
//
//				// 获取adContext
//				AdContext adContext = (AdContext) adRequest.getAdContext().clone();
//
//				try {
//					adSpaces[i] = doProcess(adRequest, impression, adContext);
//				} catch (Exception e) {
//					throw e;
//				} finally {
//					loggerService.logAdRequest(adContext);
//				}
//
//			}
//
//			data.setAdSpace(adSpaces);
//			adResponse.setStatus(ReturnCode.S0.getCode());
//			adResponse.setMessage(ReturnCode.S0.getMessage());
//		} catch (Exception e) {
//			throw e;
//		} finally {
//			adService.unbond();
//		}
	}

//	private STSAdResponse.AdSpace doProcess(STSAdRequest adRequest, STSAdRequest.Impression impression,
//			AdContext adContext) {
//
//		// 填充adContext的业务信息
//		adContext.setAdSpaceId(impression.getAid());
//
//		// 获取广告位信息
//		AdRequestCommand adRequestCommand = new AdRequestCommand();
//		adRequestCommand.setAdSpaceId(impression.getAid());
//		adRequestCommand.setaWidth(impression.getWidth());
//		adRequestCommand.setaHeight(impression.getHeight());
//
//		adRequestCommand.setScreenWidth(adContext.getScreenWidth());
//		adRequestCommand.setScreenHeight(adContext.getScreenHeight());
//		// <<<<<<<
//		// HEAD:src/main/java/cn/falconnect/adserver/adapter/web/spring/service/actions/context/STSAdProcessor.java
//
//		// HIS 历史代码: AdSpace localAdSpace =
//		// adService.fetchAdSpace(adRequestCommand);
//		AdSpace localAdSpace = adspaceService.findAdSpaceByID(adRequestCommand.getAdSpaceId());
//
//		// 生成广告位信息
//		STSAdResponse.AdSpace adSpace = new STSAdResponse.AdSpace();
//
//		adContext.setAdFormat(localAdSpace.getAdFormat().getValue());
//		adContext.setAdSpaceWidth(impression.getWidth());
//		adContext.setAdSpaceHeight(impression.getHeight());
//		adContext.setSecondOs(localAdSpace.getOs());
//		adSpace.setAid(localAdSpace.getId());
//		adSpace.setAdFormat(localAdSpace.getAdFormat().getValue());
//		Ratio ratio = localAdSpace.getRatio();
//		if (ratio != null) {
//			adContext.setAdSpaceRatio(ratio.getValue());
//		}
//
//		// 匹配策略
//		matchStrategy(localAdSpace, adContext);
//
//		if (localAdSpace.getMatchStrategy() == null) {
//			try {
//				return gdtMobileServerHandler.request(adContext, adSpace, localAdSpace, ReturnCode.E20004);
//			} catch (Exception e) {
//				if (localAdSpace.getDefaultCreative() == null) {
//					throw new AdServerMessageException(ReturnCode.E20004);
//				} else {
//					localAdSpace.setMatchCreative(localAdSpace.getDefaultCreative());
//				}
//			}
//		} else {
//			adContext.setStrategyId(localAdSpace.getMatchStrategy().getId());
//			adContext.setCampaignId(localAdSpace.getMatchStrategy().getCampaign().getId());
//		}
//
//		switch (AdFormat.adFormatMap.get(localAdSpace.getAdFormat().getValue())) {
//
//		case BANNER:
//		case TABLE_SCREEN:
//		case OPEN_SCREEN:
//
//			AdFormatHandler adFormatHandler = adFormatHandlers.get(AdFormat.OPEN_SCREEN);
//			adFormatHandler.matchCreative(localAdSpace);
//			if (localAdSpace.getMatchCreative() == null) {
//				return gdtMobileServerHandler.request(adContext, adSpace, localAdSpace, ReturnCode.E20003);
//			}
//
//			// 匹配素材
//			matchMaterial(localAdSpace, adRequestCommand);
//			if (localAdSpace.getMatchAdMaterial() == null) {
//				return gdtMobileServerHandler.request(adContext, adSpace, localAdSpace, ReturnCode.E20005);
//			}
//
//			adFormatHandler.compositeMaterial(localAdSpace, adSpace);
//
//			break;
//		case VIDEO:
//
//			adFormatHandler = adFormatHandlers.get(AdFormat.VIDEO);
//			adFormatHandler.matchCreative(localAdSpace);
//			if (localAdSpace.getMatchCreative() == null) {
//				return gdtMobileServerHandler.request(adContext, adSpace, localAdSpace, ReturnCode.E20003);
//			}
//			// 匹配素材
//			matchMaterial(localAdSpace, adRequestCommand);
//			if (localAdSpace.getMatchAdMaterial() == null) {
//				return gdtMobileServerHandler.request(adContext, adSpace, localAdSpace, ReturnCode.E20005);
//			}
//			adFormatHandler.compositeMaterial(localAdSpace, adSpace);
//			break;
//		case AD_NATIVE:
//
//			adFormatHandler = adFormatHandlers.get(AdFormat.AD_NATIVE);
//			adFormatHandler.matchCreative(localAdSpace);
//			if (localAdSpace.getMatchCreative() == null) {
//				return gdtMobileServerHandler.request(adContext, adSpace, localAdSpace, ReturnCode.E20003);
//			}
//			adFormatHandler.compositeMaterial(localAdSpace, adSpace);
//			// 设置templateId
//			adContext.setTemplateId(localAdSpace.getAdTemplate().getId());
//			break;
//		default:
//			break;
//
//		}
//
//		// 设置creativeId,materialId,maxDuration(播放时长)
//		adContext.setCreativeId(localAdSpace.getMatchCreative().getId());
//
//		if (localAdSpace.getMatchAdMaterial() != null) {
//			adContext.setMaterialId(localAdSpace.getMatchAdMaterial().getId());
//			adContext.setMaxDuration(localAdSpace.getMatchAdMaterial().getDuration());
//		}
//		
//		generateGuid(adContext, adRequest.getDevice());
//
//		/**
//		 * 拼写展示和点击url
//		 */
//		String impUrlFormat = serverAddress
//				+ "v2/imp/ch/1?platform_id=%s&sid=%s&version=%s&aid=%s&cid=%s&mid=%s&guid=%s";
//		STSAdResponse.Creative creative = adSpace.getCreative()[0];
//		String impUrl = String.format(impUrlFormat, "1", adContext.getStrategyId(), adRequest.getVersion(),
//				adContext.getAdSpaceId(), adContext.getCreativeId(), adContext.getMaterialId(), adContext.getGuid())
//				+ "&lon=%%LON%%&lat=%%LAT%%";
//
//		String[] impUrls = creative.getImpression();
//		if (impUrls != null) {
//			String[] newImpUrls = new String[impUrls.length + 1];
//			for (int k = 0; k < impUrls.length; k++) {
//				newImpUrls[k + 1] = impUrls[k];
//			}
//
//			newImpUrls[0] = impUrl;
//
//			// -----------------------top
//			// String eventValue = creative.getEvent()[0].getEventValue();
//			// if (StringUtils.hasText(eventValue)) {
//			// //添加targeturl
//			// String convertTarget =
//			// convertTargetUrlHandler.getConvertTarget(adContext, eventValue);
//			// if(StringUtils.hasText(convertTarget)){
//			// String[] impUrls2 = new String[newImpUrls.length + 1];
//			// for (int k = 0; k < newImpUrls.length; k++) {
//			// impUrls2[k] = newImpUrls[k];
//			// }
//			// impUrls2[impUrls.length] = convertTarget;
//			// newImpUrls=impUrls2;
//			// }
//			// }
//			// -----------------------bot
//			creative.setImpression(newImpUrls);
//		} else {
//
//			String[] newImpUrls = null;
//
//			// String eventValue = creative.getEvent()[0].getEventValue();
//			// if (StringUtils.hasText(eventValue)) {
//			// //添加targeturl
//			// String convertTarget =
//			// convertTargetUrlHandler.getConvertTarget(adContext, eventValue);
//			// newImpUrls = new String[]{impUrl,convertTarget};
//			// }else{
//			// newImpUrls = new String[]{impUrl};
//			// }
//			// 下面一行代码 和 上面注释 选择其一
//			newImpUrls = new String[] { impUrl };
//
//			creative.setImpression(newImpUrls);
//
//		}
//
//		String clickUrlFormat = serverAddress
//				+ "v2/click/ch/1?platform_id=%s&sid=%s&version=%s&aid=%s&cid=%s&mid=%s&guid=%s";
//		String clickUrl = String.format(clickUrlFormat, "1", adContext.getStrategyId(), adRequest.getVersion(),
//				adContext.getAdSpaceId(), adContext.getCreativeId(), adContext.getMaterialId(), adContext.getGuid())
//				+ "&lon=%%LON%%&lat=%%LAT%%&downx=%%DOWNX%%&downy=%%DOWNY%%&upx=%%UPX%%&upy=%%UPY%%&target=";
//		String eventValue = creative.getEvent()[0].getEventValue();
//		if (StringUtils.hasText(eventValue)) {
//			try {
//				String targetUrl = "";
//
//				targetUrl = URLEncoder.encode(convertTargetUrlHandler.getConvertTarget(adContext, eventValue), "UTF-8");
//
//				String extendClickUrl = convertTargetUrlHandler.convertExtendClickUrl(adContext);
//
//				clickUrl += targetUrl + extendClickUrl;
//
//			} catch (UnsupportedEncodingException e) {
//				logger.error("url encode target url error {}", e.getMessage());
//			}
//		}
//
//		creative.getEvent()[0].setEventValue(clickUrl);
//
//		return adSpace;
//	}

	/**
	 * 匹配默认策略
	 *
	 * @param adSpace
	 * @param adContext
	 * @return
	 */
	protected void matchStrategy(AdSpace adSpace, AdContext adContext) {

		// 获取广告位对应的策略集合
		List<Strategy> strategyList = adSpace.getStrategys();

		Iterator<Strategy> it = strategyList.iterator();

		while (it.hasNext()) {

			Strategy strategy = it.next();
			// 判断策略当前时间是否在策略时间内
			long time = System.currentTimeMillis();
			long startTime = strategy.getStartTime().getTime();
			long endTime = strategy.getEndTime().getTime();

			double ratio = adService.getRatio(strategy.getCampaign().getId());
			if (adSpace.getChargePrice() != null && adSpace.getChargePrice()
					.subtract(strategy.getUnitPrice().multiply(new BigDecimal(ratio))).doubleValue() > 0) {
				if (debugMode) {
					logger.info("adSpace price high over strategy price", strategy.getId());
				}
				it.remove();
				continue;
			}

			if (time < startTime || time > endTime) {
				if (debugMode) {
					logger.info("strategy ({}) is not in time range", strategy.getId());
				}
				it.remove();
				continue;
			}

			if (!matcherChain.match(strategy, adContext)) {
				if (debugMode) {
					logger.info("strategy ({}) {} target is not matched", matcherChain.getClass(), strategy.getId());
				}
				it.remove();
				continue;
			}

			if (!adService.whetherHaveBalance(strategy)) {
				if (debugMode) {
					logger.info("strategy ({}) have no balance", strategy.getId());
				}
				it.remove();
				continue;
			}

		}
		// 过滤优先级
		strategyList = priorityFilter(strategyList);

		if (strategyList.size() == 0) {
			// 选择默认策略
		} else if (strategyList.size() == 1) {
			adSpace.setMatchStrategy(strategyList.get(0));
		} else {
			adSpace.setMatchStrategy(strategyList.get(new Random().nextInt(strategyList.size())));
		}

	}

	/**
	 * 匹配素材
	 * 
	 * @param adSpace
	 */
	protected void matchMaterial(AdSpace adSpace, AdRequestCommand command) {

		AdMaterial adMaterial = chooseMaterial(adSpace.getMatchCreative().getAdMaterials(), command, adSpace);
		adSpace.setMatchAdMaterial(adMaterial);

	}

	private List<Strategy> priorityFilter(List<Strategy> strategyList) {

		if (strategyList.size() <= 1) {
			return strategyList;
		}

		int minPriority = 0;
		List<Strategy> filterStrategyList = new ArrayList<>();
		for (int i = 0; i < strategyList.size(); i++) {

			Strategy strategy = strategyList.get(i);
			int priority = strategy.getPriority();
			if (i == 0) {
				minPriority = priority;
				filterStrategyList.add(strategy);
			} else {
				if (priority < minPriority) {
					filterStrategyList.clear();
					filterStrategyList.add(strategy);
					minPriority = priority;
				} else if (priority == minPriority) {
					filterStrategyList.add(strategy);
				}
			}

		}

		return filterStrategyList;

	}

	/**
	 * 计算最符合尺寸的素材
	 */
	private AdMaterial chooseMaterial(List<AdMaterial> adMaterials, AdRequestCommand aCommand, AdSpace adSpace) {

		List<AdMaterial> fitMaterials = null;

		if (aCommand.getaWidth() > 0) {
			int width = aCommand.getaWidth();
			int height = aCommand.getaHeight();

			fitMaterials = calMaterialSize(adMaterials, width, width, height);

		} else {
			Ratio ratio = adSpace.getRatio();
			String ratioValue = ratio.getValue();
			String[] ratioSize = ratioValue.split(":");
			double width = Double.parseDouble(ratioSize[0]);
			double height = Double.parseDouble(ratioSize[1]);

			if (aCommand.getScreenWidth() > 0) {
				int screenWidth = aCommand.getScreenWidth();
				fitMaterials = calMaterialSize(adMaterials, screenWidth, width, height);
			} else {

				fitMaterials = adMaterials;

				Collections.sort(fitMaterials, new Comparator<AdMaterial>() {
					@Override
					public int compare(AdMaterial o1, AdMaterial o2) {
						return o1.getAdSize().getWidth() - o2.getAdSize().getWidth();
					}
				});

				if (!CollectionUtils.isEmpty(fitMaterials) && fitMaterials.size() > 0) {
					return fitMaterials.get(0);
				}

			}

		}

		if (fitMaterials.size() == 0) {
			throw new AdServerMessageException(ReturnCode.E20005);
		}

		return fitMaterials.get(new Random().nextInt(fitMaterials.size()));

	}

	private List<AdMaterial> calMaterialSize(List<AdMaterial> adMaterials, int thresholdWidth, double width,
                                             double height) {

		double min = 0.0d;
		boolean init = true;
		int minDivide = 0;
		List<AdMaterial> fitMaterials = new ArrayList<>();

		// 取宽度小于屏幕宽度的素材
		for (AdMaterial adMaterial : adMaterials) {
			if (adMaterial.getAdSize().getWidth() <= thresholdWidth) {
				double d = adMaterial.computeD(width, height);
				int divide = thresholdWidth - adMaterial.getAdSize().getWidth();
				d = Double.parseDouble(df.format(d));

				if (init) {
					fitMaterials.add(adMaterial);
					min = d;
					minDivide = divide;
					init = false;
				} else {
					if (d < min) {
						fitMaterials.clear();
						fitMaterials.add(adMaterial);
						min = d;
						minDivide = divide;
					} else if (d == min) {
						if (divide == minDivide) {
							fitMaterials.add(adMaterial);
						}

						if (divide < minDivide) {
							fitMaterials.clear();
							fitMaterials.add(adMaterial);
							minDivide = divide;
						}
					}
				}
			}
		}

		return fitMaterials;
	}

//	/**
//	 * 生成GUID
//	 * 
//	 * @param adContext
//	 * @param device
//	 */
//	private void generateGuid(AdContext adContext, STSAdRequest.Device device) {
//
//		// 生成guid
//		GuidValueObject guidVo = new GuidValueObject();
//		guidVo.setAid(adContext.getAdSpaceId());
//		guidVo.setSid(adContext.getStrategyId());
//		guidVo.setCid(adContext.getCreativeId());
//		guidVo.setCampaignId(adContext.getCampaignId());
//		guidVo.setMid(adContext.getMaterialId());
//		guidVo.setMuniqueId(adContext.getMuniqueId());
//		guidVo.setMuniqueType(adContext.getMuniqueType());
//		guidVo.setBrand(adContext.getBrand());
//		guidVo.setOs(adContext.getOs());
//		guidVo.setPlatform(adContext.getModel());
//		guidVo.setDeviceSize(device.getDeviceSize());
//		guidVo.setNetwork(adContext.getConnType());
//		guidVo.setOperator(adContext.getMob_ops());
//		guidVo.setIp(adContext.getIp());
//		guidVo.setLongitude(adContext.getLongitude());
//		guidVo.setLatitude(adContext.getLatitude());
//
//		guidVo.setMac(adContext.getMac());
//		guidVo.setMac1(adContext.getMac1());
//		guidVo.setImei(adContext.getImei());
//		guidVo.setAnid(adContext.getAndroidId());
//		guidVo.setAaid(adContext.getAaid());
//		guidVo.setIdfa(adContext.getIdfa());
//		guidVo.setUdid(adContext.getUdid());
//		guidVo.setDuid(adContext.getDuid());
//
//		adContext.setGuid(adService.storeGuid(guidVo, 1));
//	}

	/**
	 * 填充TrackCommand
	 * 
	 * 
	 * 注：STSAdRequestAction，STSAdProcessor，ImpressionHandler，ImpressionMatchService
	 * 中为同一份,有点不同。代码冗余。
	 * 
	 * @param bidContext
	 * @param command
	 * @return
	 */
	protected TrackCommand populateCommand(AdContext bidContext, TrackCommand command) {
		if (command == null) {
			command = new TrackCommand();
		}
		// os={os}&mac={mac}&mac_md5={mac_md5}&idfa={idfa}
		// &idfa_md5={idfa_md5}&imei={imei}&imei_md5={imei_md5}&
		// androidid={androidid}&androidid_md5={androidid_md5}

		// &aid={aid}&sid={sid}&cid={cid}&mid={mid}&guid={guid}
		command.setOs(bidContext.getOs());
		command.setMac("");
		command.setMac_md5(bidContext.getMac());
		command.setIdfa("");
		command.setIdfa_md5(bidContext.getIdfa());
		command.setImei("");
		command.setImei_md5(bidContext.getImei());
		command.setAndroidid("");
		command.setAndroidid_md5(bidContext.getAndroidId());
		command.setAid(bidContext.getAdSpaceId());
		command.setSid(bidContext.getStrategyId());
		command.setCid(bidContext.getCreativeId());
		command.setMid(bidContext.getMaterialId());
		command.setGuid(bidContext.getGuid());
		return command;
	}

}
